#上一个自动密码生成器是采用一种暴力破解，穷举的方式，而现在我们要更加聪明一点
#比如先输出几个数字，再输出几个大写字母，再写几个小写字母，再写几个字符
#版本更新1：智能密码生成器，使用random中choice，sample来进行密码的选取，使用random，shuffle对函数进行乱序排序
#版本更新2：利用切片来消除string.printable中的后几位不可见字符，如换行，制表
#版本更新3：利用函数的参数，来增加迷惑字符（il） confuse
#版本更新4：让用户通过命令行控制 密码长度 和 是否加入迷惑字符

#4-1：通过argparse 来实现 （用户友好的命令行窗口）
                                   #也就是说：像c那样，用input实现是不行的了


#输出的结果：
#新生成的密码为：=5_m
#J4^~

#对结果进行分析：
#哎！很奇怪，密码还换行了呢？
#import string
#print(string.printable)
#0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
#
#哦！printable中后几位是包含换行\n,制表\t,等字符，怎么办呢？
                                            #用切片！！！
    #result += ''.join(random.sample(string.printable[:-6]*password_length, k=password_length-4))
###同时对第二个密码生成器中的bug进行了更改！

import string
import random
import argparse

def yanzheng_password(password,show_info):
    result = False

    password_state = 0b00000

    for char in password:
        if char.isupper():
            password_state = password_state | 0b10000
        elif char.islower():
            password_state |= 0b01000
        elif char.isdigit():
            password_state |= 0b00100
        else:
            password_state |= 0b00010
    if len(password) >= 8:
        password_state |= 0b00001

    #todo:输出反馈：

    if password_state == 0b11111:
        if show_info:
            print('密码符合要求！')

        result = True

    else:
        if show_info:
            tishi = '密码不符合要求，'
            if password_state & 0b00001 == 0:
                tishi = tishi + '长度不足8，'
            if password_state & 0b10000 == 0:
                tishi = tishi + '没有包含大写字符，'
            if password_state & 0b01000 == 0:
                tishi += '没有包含小写字符，'
            if password_state & 0b00100 == 0:
                tishi += '没有包含数字，'
            if password_state & 0b00010 == 0:
                tishi += '没有包含标点,'

            tishi = tishi[:-1]

            print(tishi)


    return (result)


def main_userimput():
    while 1:

        #TODO:用户输入密码
        user_password = input('请输入密码:')

        #TODO：判断密码的合法性(只是判断)

        if yanzheng_password(user_password):
            break

def generate_password():

    all_char_set = string.printable[:-6]

    all_char_set = string.printable

    all_char_set *= 9
    result = ''.join(random.sample(all_char_set,k=9))
    return (result)


def mian_generate():
    while 1:

        #TODO:电脑来随机生成密码
        user_password = generate_password()      #

        #TODO：判断密码的合法性(只是判断)

        if yanzheng_password(user_password,show_info=False):
            ###要输出，符合要求的密码
             ##引入 f字符串的使用
            print(f"新生成的密码为：{user_password}")
            break

def smartgen_password(password_length,confuse=False):                 #利用函数参数，实现密码长度的可选定化
    #todo:生成指定长度的包含四类字符的密码
###2
           #在四个类别里取一个元素
           #先想：random.sample(string.ascii_uppercase,k=1) 这样可以实现
           #查标准库：哦！发现有choice更适合，它本身就是从一个序列里（并没有指定序列类型）随机取一个，更方便
                                 #举个例子：random.choice('1234567890')
    result = ''     #首先进行初始化
    result += random.choice(string.ascii_uppercase)
    result += random.choice(string.ascii_lowercase)
    result += random.choice(string.digits)
    result += random.choice(string.punctuation)
    #到此，已经指定了4个字符，让其符合了除长度以外的条件，剩下的就随机选择就好了,用sample，同时可以控制长度
    if confuse:

        result += 'Il'
        result += ''.join(random.sample(string.printable[:-6] * password_length, k=password_length-6))
    else:
        result += ''.join(random.sample(string.printable[:-6] * password_length, k=password_length-4))
        #else后面 要加 : (冒号)

                          #这里还可以考虑重复行问题，让其乘以其（大于其密码的长度）让其可重复
    #还要特别注意的是：result可以相加的前提是要都为同一类型，而sample输出是列表，所以要用join 将sample后的列表进行转化
                                                                       #所以choice后的结果是 字符类型
    #todo:对生成的密码进行随机打乱
                     #先去查标准库
    result =''.join(random.sample(result, k=len(result))) #对字符串类型的result 进行它自身的重新排序
                                                                              #！！！
                                                #排序完，仍要记得将这一个sample的结果，转化成字符串的形式来返回
    return (result)


###1
#查到标准库里有：random.shuffle() 用来打乱列表中的顺序 首先[] 构成的的东西是列表，()构成的东西是元组 "" '' 双引号或者单引号之间是字符串
              #random.sample 和 random.shuffle不同
              #shuffle只能是 将列表中的元素打乱顺序 没有另外创建一个列表（开辟空间） 改变完顺序，也仍是原来的那一个列表（那一个空间）
                      #元组内的顺序是无法由它来改变的（本质：shuffle只能对其中顺序是可变的变量类型进行操作）
                 #所以 字符串 也不能由shuffle进行操作   “hjfahkdh" "" '' 双引号或者单引号之间是字符串
    #举例：
    #a = [1,2,3,4,5] 列表
    #random.shuffle(a)
              #而sample是在原来（容器）的基础上创建一个新的列表，将改变完顺序的元素放在这个新的（容器）之中
                                  #列表 和 元组 都可以当作一个容器
              #所以sample 既可以改变列表[] 也可以该改变元组() 也可以改变字符串”“
                                                     #本质：无论变量类型内顺序可不可以变化，sample都可以对其进行处理
                                                                    #这也就可以理解为什么sample处理后，输出结果都为列表
                                        #大胆猜想，sample内部是首先对这些元素进行列表化，再排序，输出，就为列表了
    #举例：
    #a = (1,2,3,4,5) 元组
    #random.sample(a, k=len(a))      ！！！注意，一般来说，给函数赋值的等号左右不加空格
                                                               #除此之外，等号左右要加空格
    #输出结果：
    #[1,3,2,5,4] 注意，a之前是元组，sample处理之后变为列表
    #让其以字符串的形式出现：
        #''.join(random.sample(a,k=len(a))
    #同理：
    #’‘。join(random.sample("asdfghj",len("asdfghj")))


def main_smartgen():
    #for i in range(100):     #用for循环进行批量输出，已检测程序是否有问题
    #注意：修改完代码要记得调试之后再，运行，否则可能出现电脑运行之前没修改的程序，以至于忽略问题的修改，造成错误
    parser = argparse.ArgumentParser(description='Generate new password.')
    parser.add_argument('-l','--length',type=int,default=9,
                       help='length of password(default: 9)')
    parser.add_argument('-c','--confuse',action='store_true',
                       help='use confuse characters (I & l)')
    args = parser.parse_args()
    for i in range(1):
        print(f"新生成的密码为：{smartgen_password(args.length,args.confuse)}")



#main_userimput()
#mian_generate()
main_smartgen()
#对用户友好的命令行函数
#在终端，加入程序地址，后面加 -h
           #得到已经设计好的帮助提示
#分别设置了两组参数，-l(短参数),要对应跟着数据类型，--length(长参数),要对应跟着对应数据类型要求的值
               # -c(短参数),它好像没有跟数据类型，--confuse(长参数),对应跟了action 和 表示True 的特殊变量
                                                                             #store_true
#在终端
     #输入：-l (这里填一个数字) 即可实现控制密码的长度
     #输入：-c 则默认要加迷惑字符
         #若不输入 -c 则默认不加入迷惑字符

#关于这个什么用户友好的命令行函数，还需要再次学习
      ###？？？ 是不是像 c语言 直接通过scanf函数来输入一个值作为函数的参数 的这种类似的方式在，python中不可行呢？
      #input函数，输入到电脑里的是什么啊？估计是字符，而非整型(根据之前实验的猜测)